{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "821fbdd7",
   "metadata": {},
   "source": [
    "# MarkdownHeaderTextSplitter\n",
    "\n",
    "마크다운 파일의 구조를 이해하고 효율적으로 다루는 것은 문서 작업에 있어 매우 중요할 수 있습니다. 특히, 문서의 전체적인 맥락과 구조를 고려하여 의미 있는 방식으로 텍스트를 임베딩하는 과정은, 광범위한 의미와 주제를 더 잘 포착할 수 있는 포괄적인 벡터 표현을 생성하는 데 큰 도움이 됩니다.\n",
    "\n",
    "이러한 맥락에서, 마크다운 파일의 특정 부분, 즉 헤더별로 내용을 나누고 싶을 때가 있습니다. 예를 들어, 문서 내에서 각각의 헤더 아래에 있는 내용을 기반으로 서로 연관된 정보 덩어리, 즉 '청크'를 만들고 싶은 경우가 그러합니다. 이는 텍스트의 공통된 맥락을 유지하면서도, 문서의 구조적 요소를 효과적으로 활용하려는 시도입니다.\n",
    "\n",
    "이런 과제를 해결하기 위해, `MarkdownHeaderTextSplitter` 라는 도구를 활용할 수 있습니다. 이 도구는 문서를 지정된 헤더 집합에 따라 분할하여, 각 헤더 그룹 아래의 내용을 별도의 청크로 관리할 수 있게 합니다. 이 방법을 통해, 문서의 전반적인 구조를 유지하면서도 내용을 더 세밀하게 다룰 수 있게 되며, 이는 다양한 처리 과정에서 유용하게 활용될 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dcdcab6e",
   "metadata": {},
   "source": [
    "`MarkdownHeaderTextSplitter`를 사용하여 마크다운 형식의 텍스트를 헤더 단위로 분할합니다.\n",
    "\n",
    "- 마크다운 문서의 헤더(`#`, `##`, `###` 등)를 기준으로 텍스트를 분할하는 역할을 합니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7eb996fe",
   "metadata": {},
   "source": [
    "- `markdown_document` 변수에 마크다운 형식의 문서가 할당됩니다.\n",
    "- `headers_to_split_on` 리스트에는 마크다운 헤더 레벨과 해당 레벨의 이름이 튜플 형태로 정의됩니다.\n",
    "- `MarkdownHeaderTextSplitter` 클래스를 사용하여 `markdown_splitter` 객체를 생성하며, `headers_to_split_on` 매개변수로 분할 기준이 되는 헤더 레벨을 전달합니다.\n",
    "- `split_text` 메서드를 호출하여 `markdown_document`를 헤더 레벨에 따라 분할합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af2fce82",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_text_splitters import MarkdownHeaderTextSplitter\n",
    "\n",
    "# 마크다운 형식의 문서를 문자열로 정의합니다.\n",
    "markdown_document = \"# Title\\n\\n## 1. SubTitle\\n\\nHi this is Jim\\n\\nHi this is Joe\\n\\n### 1-1. Sub-SubTitle \\n\\nHi this is Lance \\n\\n## 2. Baz\\n\\nHi this is Molly\"\n",
    "print(markdown_document)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d4cf02f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "headers_to_split_on = [  # 문서를 분할할 헤더 레벨과 해당 레벨의 이름을 정의합니다.\n",
    "    (\n",
    "        \"#\",\n",
    "        \"Header 1\",\n",
    "    ),  # 헤더 레벨 1은 '#'로 표시되며, 'Header 1'이라는 이름을 가집니다.\n",
    "    (\n",
    "        \"##\",\n",
    "        \"Header 2\",\n",
    "    ),  # 헤더 레벨 2는 '##'로 표시되며, 'Header 2'라는 이름을 가집니다.\n",
    "    (\n",
    "        \"###\",\n",
    "        \"Header 3\",\n",
    "    ),  # 헤더 레벨 3은 '###'로 표시되며, 'Header 3'이라는 이름을 가집니다.\n",
    "]\n",
    "\n",
    "# 마크다운 헤더를 기준으로 텍스트를 분할하는 MarkdownHeaderTextSplitter 객체를 생성합니다.\n",
    "markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)\n",
    "# markdown_document를 헤더를 기준으로 분할하여 md_header_splits에 저장합니다.\n",
    "md_header_splits = markdown_splitter.split_text(markdown_document)\n",
    "# 분할된 결과를 출력합니다.\n",
    "for header in md_header_splits:\n",
    "    print(f\"{header.page_content}\")\n",
    "    print(f\"{header.metadata}\", end=\"\\n=====================\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eca626e1",
   "metadata": {},
   "source": [
    "# Title\n",
    "\n",
    "## 1. SubTitle\n",
    "\n",
    "Hi this is Jim\n",
    "\n",
    "Hi this is Joe\n",
    "\n",
    "### 1-1. Sub-SubTitle \n",
    "\n",
    "Hi this is Lance \n",
    "\n",
    "## 2. Baz\n",
    "\n",
    "Hi this is Molly"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdd4bf3f",
   "metadata": {},
   "source": [
    "기본적으로 `MarkdownHeaderTextSplitter`는 분할되는 헤더를 출력 청크의 내용에서 제거합니다.\n",
    "\n",
    "이는 `strip_headers = False`로 설정하여 비활성화할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "942acf87",
   "metadata": {},
   "outputs": [],
   "source": [
    "markdown_splitter = MarkdownHeaderTextSplitter(\n",
    "    # 분할할 헤더를 지정합니다.\n",
    "    headers_to_split_on=headers_to_split_on,\n",
    "    # 헤더를 제거하지 않도록 설정합니다.\n",
    "    strip_headers=False,\n",
    ")\n",
    "# 마크다운 문서를 헤더를 기준으로 분할합니다.\n",
    "md_header_splits = markdown_splitter.split_text(markdown_document)\n",
    "# 분할된 결과를 출력합니다.\n",
    "for header in md_header_splits:\n",
    "    print(f\"{header.page_content}\")\n",
    "    print(f\"{header.metadata}\", end=\"\\n=====================\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9184ecb2",
   "metadata": {},
   "source": [
    "각 마크다운 그룹 내에서는 원하는 텍스트 분할기(text splitter)를 적용할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1d8e99ee",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
    "\n",
    "markdown_document = \"# Intro \\n\\n## History \\n\\nMarkdown[9] is a lightweight markup language for creating formatted text using a plain-text editor. John Gruber created Markdown in 2004 as a markup language that is appealing to human readers in its source code form.[9] \\n\\nMarkdown is widely used in blogging, instant messaging, online forums, collaborative software, documentation pages, and readme files. \\n\\n## Rise and divergence \\n\\nAs Markdown popularity grew rapidly, many Markdown implementations appeared, driven mostly by the need for \\n\\nadditional features such as tables, footnotes, definition lists,[note 1] and Markdown inside HTML blocks. \\n\\n#### Standardization \\n\\nFrom 2012, a group of people, including Jeff Atwood and John MacFarlane, launched what Atwood characterised as a standardisation effort. \\n\\n# Implementations \\n\\nImplementations of Markdown are available for over a dozen programming languages.\"\n",
    "print(markdown_document)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93f8615c",
   "metadata": {},
   "source": [
    "먼저, `MarkdownHeaderTextSplitter` 사용하여 마크다운 문서를 헤더를 기준으로 분할합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bbaa36a9",
   "metadata": {},
   "outputs": [],
   "source": [
    "headers_to_split_on = [\n",
    "    (\"#\", \"Header 1\"),  # 분할할 헤더 레벨과 해당 레벨의 이름을 지정합니다.\n",
    "    # (\"##\", \"Header 2\"),\n",
    "]\n",
    "\n",
    "# Markdown 문서를 헤더 레벨에 따라 분할합니다.\n",
    "markdown_splitter = MarkdownHeaderTextSplitter(\n",
    "    headers_to_split_on=headers_to_split_on, strip_headers=False\n",
    ")\n",
    "md_header_splits = markdown_splitter.split_text(markdown_document)\n",
    "# 분할된 결과를 출력합니다.\n",
    "for header in md_header_splits:\n",
    "    print(f\"{header.page_content}\")\n",
    "    print(f\"{header.metadata}\", end=\"\\n=====================\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d7d0e15",
   "metadata": {},
   "source": [
    "이전의 `MarkdownHeaderTextSplitter` 로 분할된 결과를 다시 `RecursiveCharacterTextSplitter` 로 분할합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3f9cd8ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunk_size = 200  # 분할된 청크의 크기를 지정합니다.\n",
    "chunk_overlap = 20  # 분할된 청크 간의 중복되는 문자 수를 지정합니다.\n",
    "text_splitter = RecursiveCharacterTextSplitter(\n",
    "    chunk_size=chunk_size, chunk_overlap=chunk_overlap\n",
    ")\n",
    "\n",
    "# 문서를 문자 단위로 분할합니다.\n",
    "splits = text_splitter.split_documents(md_header_splits)\n",
    "# 분할된 결과를 출력합니다.\n",
    "for header in splits:\n",
    "    print(f\"{header.page_content}\")\n",
    "    print(f\"{header.metadata}\", end=\"\\n=====================\\n\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py-test",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
